<template>
  <div class="header">
    <div class="title">{{ localCity }}</div>
    <div class="elform">
      <el-form :model="shapeType" class="form-items">
        <el-form-item class="form-item">
          <el-input
            @change="handleSelect"
            v-model="shapeType.selectCity"
            placeholder="请搜索城市"
          ></el-input>
        </el-form-item>
        <el-form-item class="form-item">
          <el-select v-model="shapeType.brush" placeholder="选择绘制图形">
            <el-option label="直线" value="LineString"></el-option>
            <el-option label="多边形" value="Polygon"></el-option>
            <el-option label="自由画笔" value="freedom"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item class="form-item">
          <el-select v-model="shapeType.mark" placeholder="选择标记类型">
            <el-option label="充电桩" value="pile"></el-option>
            <el-option label="停车场" value="parking"></el-option>
            <el-option label="餐厅" value="eatting"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
    </div>
  </div>
  <div id="map"></div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';
import axios from 'axios';
import { ElMessage } from 'element-plus';
import 'element-plus/dist/index.css';
import { Feature, Map, Overlay, View } from 'ol';
import TileLayer from 'ol/layer/Tile';
import { Cluster, XYZ } from 'ol/source';
import { getCityByip, getAdcodeBycity } from '@/api/index.js';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { defaults as defaultControl } from 'ol/control';
import GeoJSON from 'ol/format/GeoJSON.js';
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import { getCenter } from 'ol/extent';
import Draw from 'ol/interaction/Draw.js';
import { getLength, getArea, getDistance } from 'ol/sphere';
import { Point } from 'ol/geom';
import Icon from 'ol/style/Icon';
import ol_layer_AnimatedCluster from 'ol-ext/layer/AnimatedCluster';
import CircleStyle from 'ol/style/Circle.js';
import Text from 'ol/style/Text';
// 输入框双向绑定
const shapeType = ref({
  selectCity: '',
  brush: '',
  mark: '',
});
const localCity = ref(''); //本地城市
const isDrawing = ref(false); // 是否处于画笔绘制状态
const isMarking = ref(false); // 是否处于标记添加状态

let map;
let view = new View({
  center: [114.3123, 30.524], // 初始中心点
  projection: 'EPSG:4326',
  zoom: 4, //初始时最大
});
let chinaLayer;
let clusterSource; //聚合数据源
let ClusterLayer;

// 地图视图动画函数
const viewAnimate = (center) => {
  view.animate({
    center: center,
    duration: 1000,
    zoom: 8,
  });
};

// 根据城市更新图层
const handleSelect = async (cityName) => {
  try {
    // 请求城市adcode
    const city = await getAdcodeBycity(cityName);
    // 如果没有数据,直接返回
    if (city.status !== '1') {
      ElMessage.error('请正确输入城市名称');
      return;
    }
    const adcode = city.geocodes[0].adcode;
    localCity.value = cityName; //更新本地名称
    // 创建新的数据源
    const newSource = new VectorSource({
      url: `https://geo.datav.aliyun.com/areas_v3/bound/${adcode}.json`,
      format: new GeoJSON(),
    });
    chinaLayer.setSource(newSource); // 更新数据源
    // 设置加载状态
    ElMessage({
      type: 'info',
      message: `正在加载${cityName}...`,
      duration: 1000,
    });
    //监听数据源加载完成事件
    newSource.on('change', () => {
      //获取新的范围并计算中心点
      const extent = newSource.getExtent();
      const center = getCenter(extent);
      //调用视口动画
      viewAnimate(center);
    });
  } catch (error) {
    ElMessage.error('请求有误,请检查');
    console.log('error', error);
  }
};

// 监听change:resolution事件,控制图层显示隐藏
const handleZoomChange = () => {
  const currentZoom = view.getZoom(); // 通过 view.getZoom() 获取当前缩放级别
  const shouldShowLayer = currentZoom <= 10 ? true : false;
  chinaLayer.setVisible(shouldShowLayer);
};
view.on('change:resolution', handleZoomChange);

// 新建一个图层绘画
const drawVectorLayer = new VectorLayer({
  source: new VectorSource(),
});

// 创建交互式画笔
let draw = new Draw({
  type: 'LineString',
  source: drawVectorLayer.getSource(),
});

// 监听数据改变,销毁之前画笔,根据条件创建新画笔,同时监听画笔drawend事件,完成测距
watch(
  () => shapeType.value.brush,
  (newBrush) => {
    if (newBrush) {
      //激活绘制状态,禁用标记状态
      isMarking.value = false;
      isDrawing.value = true;
    }
    if (isDrawing.value) {
      //处于激活状态才能开始绘制
      if (draw) map.removeInteraction(draw); //移除之前画笔,重新创建交互式画笔
      let brush = shapeType.value.brush;
      if (brush == 'Polygon') {
        draw = new Draw({
          type: 'Polygon',
          source: drawVectorLayer.getSource(),
        });
      } else if (brush == 'LineString') {
        draw = new Draw({
          type: 'LineString',
          source: drawVectorLayer.getSource(),
        });
      } else if (brush == 'freedom') {
        console.log('自由画笔');
        draw = new Draw({
          type: 'LineString',
          source: drawVectorLayer.getSource(),
          freehand: true,
        });
      }
      map.addInteraction(draw); //添加新画笔
      //draw = newDraw; //重点:把当前交互替换为新创建的交互,以确保后续移除的都是上一次的交互

      // 测距功能
      draw.on('drawend', (e) => {
        // 创建overlay
        let overlay = new Overlay({});
        // 创建dom容器并添加样式
        const overlayDom = document.createElement('div');
        overlayDom.className = 'overlay';
        overlayDom.style.backgroundColor = 'pink';

        document.body.appendChild(overlayDom);
        overlay.setElement(overlayDom); //设置容器
        map.addOverlay(overlay); //添加覆盖物
        // 获取元素和几何形状
        let f = e.feature;
        let g = f.getGeometry();
        // 根据条件判断计算方法
        if (brush == 'LineString') {
          let length = getLength(g, { projection: 'EPSG:4326' });
          length = (length / 1000).toFixed(1);
          overlayDom.innerHTML = '测量的距离是：' + length + 'km';
          let coordinate = g.getCoordinates()[g.getCoordinates().length - 1];
          overlay.setPosition(coordinate);
        } else if (brush == 'Polygon') {
          let area = getArea(g, { projection: 'EPSG:4326' });
          area = area.toFixed(1);
          overlayDom.innerHTML = '测量的面积是：' + area + 'm²';
          let coordinate =
            g.getCoordinates()[g.getCoordinates().length - 1][
              g.getCoordinates().length - 1
            ];
          overlay.setPosition(coordinate);
        }
      });
    }
  }
);
// 监听标记切换时,激活标记状态,禁用绘制状态
watch(
  () => shapeType.value.mark,
  (newMark) => {
    if (newMark) {
      // 激活状态
      shapeType.value.brush = '';
      isDrawing.value = false;
      isMarking.value = true;
      // 同时销毁画笔
      if (draw) map.removeInteraction(draw);
    } else {
      isMarking.value = false;
    }
  }
);

// 封装创建要素函数
function createFeature(coordinate, type) {
  let point = new Feature({
    geometry: new Point(coordinate),
    title: `${type}`,
  });
  point.setStyle(
    new Style({
      image: new Icon({
        src: `/${type}.png`,
        width: 40,
        height: 40,
      }),
    })
  );
  return point;
}
// 聚合样式函数
function clusterStyle() {
  return (f) => {
    const size = f.get('features').length;
    let style = new Style();
    if (size === 1) {
      const feature = f.get('features')[0]; //获取单个要素
      const iconStyle = feature.getStyle(); //获取这个要素的style
      // 设置单个icon样式
      if (iconStyle) {
        style = iconStyle;
      } else {
        const title = feature.get('title');
        style.setImage(
          new Icon({
            src: `/${title}.png`,
          })
        );
      }
    } else {
      style.setImage(
        new CircleStyle({
          radius: 15,
          stroke: new Stroke({
            color: '#fff',
          }),
          fill: new Fill({
            color: '#3399CC',
          }),
        })
      );
      style.setText(
        new Text({
          text: size.toString(),
          fill: new Fill({
            color: '#fff',
          }),
        })
      );
    }
    return style;
  };
}
// 创建聚合数据源
const ClusterSource = new VectorSource();
clusterSource = new Cluster({
  source: ClusterSource,
  distance: 40,
});
// 创建聚合图层
ClusterLayer = new ol_layer_AnimatedCluster({
  name: 'Cluster',
  source: clusterSource,
  style: clusterStyle(),
});

onMounted(async () => {
  map = new Map({
    target: 'map',
    view: view,
    layers: [],
    controls: defaultControl({
      zoom: false,
      rotate: false,
      attribution: false,
    }),
  });
  // 添加底图图层
  const tileLayer = new TileLayer({
    source: new XYZ({
      url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}',
    }),
  });
  map.addLayer(tileLayer);

  // 通过请求,获得城市和adcode
  const res = await getCityByip();
  const c = res.city;
  localCity.value = c;
  const city = await getAdcodeBycity(c);
  const adcode = city.geocodes[0].adcode;

  // 通过adcode创建矢量图层
  chinaLayer = new VectorLayer({
    source: new VectorSource({
      url: `https://geo.datav.aliyun.com/areas_v3/bound/${adcode}.json`,
      format: new GeoJSON(),
    }),
    style: new Style({
      stroke: new Stroke({ color: '#333', width: 2 }),
      fill: new Fill({ color: 'rgba(255,0,0,0.2)' }),
    }),
  });
  map.addLayer(chinaLayer);

  // 监听数据源加载完成事件:然后加载动画
  chinaLayer.getSource().on('change', () => {
    const extent = chinaLayer.getSource().getExtent(); // 获取数据源的范围
    viewAnimate(getCenter(extent)); //视角动画
  });

  // 添加画笔图层,交互画笔由watch初始化创建
  map.addLayer(drawVectorLayer);

  // 添加聚合图层
  map.addLayer(ClusterLayer);
  // 监听地图点击事件,当zoom>13的时候添加标记要素
  map.on('click', (e) => {
    if (isDrawing.value || !isMarking.value) return; // 绘制中或非标记模式，返回
    const zoom = view.getZoom();
    if (zoom >= 13) {
      const coordinate = e.coordinate; //位置
      const markType = shapeType.value.mark;
      // 根据type类型创建要素并添加到图层
      if (markType == 'pile') {
        let feature = createFeature(coordinate, 'pile');
        clusterSource.getSource().addFeature(feature);
      } else if (markType == 'parking') {
        let feature = createFeature(coordinate, 'parking');
        clusterSource.getSource().addFeature(feature);
      } else if (markType == 'eatting') {
        let feature = createFeature(coordinate, 'eatting');
        clusterSource.getSource().addFeature(feature);
      }
    }
  });
});
</script>

<style lang="scss">
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
.btn {
  position: absolute;
  top: 60px;
  left: 20px;
  button {
    font-size: 20px;
  }
}
.btn.isshow {
  display: none;
}
.header {
  display: flex;
  align-items: center;
  height: 10vh;
  gap: 200px;
  padding: 10px 60px;
  background: rgb(29, 107, 223);
  .title {
    color: white;
    font-size: 22px;
  }
  .form-items {
    display: flex;
    align-items: center;
    width: 1000px;
    gap: 100px;
    .form-item {
      flex: 1;
    }
  }
}
#map {
  height: 90vh;
  width: 100vw;
}
</style>
